package ops

import (
	"errors"
	"fmt"
	"github.com/olivere/elastic/v7"
	"gluenet/pkg/gerrors"
	"gluenet/pkg/meta"
	"gluenet/pkg/ops/orm"
)

func (r *DefaultDB) ConfigGet(platform string, label string, data **meta.Config) error {
	var cfgs []meta.Config
	if err := orm.ConfigGet(r, platform, label, &cfgs); err != nil {
		return err
	}
	if len(cfgs) == 0 {
		return gerrors.DataNotFind
	}
	*data = &cfgs[0]
	return nil
}

func (r *DefaultDB) ConfigKindList(kind string, data *[]meta.Config) error {
	var cfgs []meta.Config
	if err := orm.ConfigKindListTotal(r, kind, &cfgs); err != nil {
		return err
	}
	*data = cfgs
	return nil
}

func (r *DefaultDB) ConfigList(platform string, kind string, data *[]meta.Config) error {
	var cfgs []meta.Config
	if err := orm.ConfigList(r, platform, kind, &cfgs); err != nil {
		return err
	}
	*data = cfgs
	return nil
}

func (r *DefaultDB) ConfigListAll(data *[]meta.Config) error {
	//TODO implement me
	var cfgs []meta.Config
	if err := orm.ConfigListTotal(r, &cfgs); err != nil {
		return err
	}
	*data = cfgs
	return nil
}

func (r *DefaultDB) ConfigDel(label string) error {
	return orm.ConfigDelete(r, label)
}

func (r *DefaultDB) InstanceGet(guid string, data **meta.Instance) error {
	var ins []meta.Instance
	if err := orm.InstanceGet(r, guid, &ins); err != nil {
		return err
	}

	if len(ins) == 0 {
		return gerrors.DataNotFind
	}
	*data = &ins[0]
	return nil
}

func (r *DefaultDB) InstanceList(platform string, kind string, data *[]meta.Instance) error {
	var ins []meta.Instance
	if err := orm.InstanceList(r, platform, kind, &ins); err != nil {
		return err
	}
	*data = ins
	return nil
}

func (r *DefaultDB) InstanceKindList(kind string, data *[]meta.Instance) error {
	var ins []meta.Instance
	if err := orm.InstanceKindList(r, kind, &ins); err != nil {
		return err
	}
	*data = ins
	return nil
}

func (r *DefaultDB) InstanceListAll(data *[]meta.Instance) error {
	var ins []meta.Instance
	if err := orm.InstanceListAll(r, &ins); err != nil {
		return err
	}
	*data = ins
	return nil
}

func (r *DefaultDB) InstanceDel(guid string) error {
	return orm.InstanceDelete(r, guid)
}

func (r *DefaultDB) InstanceRuntimeGet(guid string, data **meta.InstanceRunTime) error {
	var irun []meta.InstanceRunTime
	if err := orm.InstanceRunTimeGet(r, guid, &irun); err != nil {
		return err
	}
	if len(irun) == 0 {
		return gerrors.DataNotFind
	}
	*data = &irun[0]
	return nil
}

func (r *DefaultDB) InstanceRuntimeList(data *[]meta.InstanceRunTime) error {
	var irun []meta.InstanceRunTime
	if err := orm.InstanceRunTimeList(r, &irun); err != nil {
		return err
	}
	if len(irun) == 0 {
		return gerrors.DataNotFind
	}
	*data = irun
	return nil
}

func (r *DefaultDB) InstanceRuntimePut(data *meta.InstanceRunTime) error {
	return orm.PutInstanceRuntime(r, data)
}

func (r *DefaultDB) InstanceRuntimeContainerUpdate(guid string, containers string) error {
	return orm.UpdateInstancesRuntimeContainers(r, guid, containers)
}

func (r *DefaultDB) InstanceRuntimeContainerInfoUpdate(guid string, containers string) error {
	return orm.UpdateInstancesRuntimeContainersInfo(r, guid, containers)
}

func (r *DefaultDB) InstanceRuntimeContainerTracesGet(guid string, data *string) error {
	var res []string
	if err := orm.InstanceRunTimeContainerTracesGet(r, guid, &res); err != nil {
		return err
	}
	if len(res) == 0 {
		return gerrors.DataNotFind
	}
	*data = res[0]
	return nil
}

func (r *DefaultDB) InstanceRuntimeContainerTracesUpdate(guid string, data string) error {
	return orm.UpdateInstanceRuntimeContainersTraces(r, guid, data)
}

func (r *DefaultDB) InstanceRuntimeStatusUpdate(guid string, status string) error {
	return orm.UpdateInstancesRuntimeStatus(r, guid, status)
}

func (r *DefaultDB) InstanceStatusGet(guid string, data **meta.InstanceStatus) error {
	containers, state, err := orm.GetInstanceContainerAndState(r, guid)
	//fmt.Printf("containers %v \n state: %v\n", containers, state)
	if err != nil {
		return err
	}
	*data = &meta.InstanceStatus{
		Contaienrs: containers.(map[string]map[string]interface{}),
		State:      state,
	}
	return nil
}

func (r *DefaultDB) AgentGet(guid string, data **meta.Agent) error {
	var ags []meta.Agent
	if err := orm.AgentGet(r, guid, &ags); err != nil {
		return err
	}
	if len(ags) == 0 {
		return gerrors.DataNotFind
	}
	*data = &ags[0]
	return nil
}

func (r *DefaultDB) AgentListAll(data *[]meta.Agent) error {
	var agents []meta.Agent
	if err := orm.AgentList(r, &agents); err != nil {
		return err
	}
	*data = agents
	return nil
}

func (r *DefaultDB) AgentRuntimeGet(guid string, data **meta.AgentRunTime) error {
	var agentRuntime []meta.AgentRunTime
	if err := orm.GetAgentRuntime(r, guid, &agentRuntime); err != nil {
		return err
	}
	if len(agentRuntime) == 0 {
		return gerrors.DataNotFind
	}
	*data = &agentRuntime[0]
	return nil
}

func (r *DefaultDB) AgentRuntimeList(data *[]meta.AgentRunTime) error {
	var agentRuntime []meta.AgentRunTime
	if err := orm.ListAgentRuntime(r, &agentRuntime); err != nil {
		return err
	}
	if len(agentRuntime) == 0 {
		return gerrors.DataNotFind
	}
	*data = agentRuntime
	return nil
}

func (r *DefaultDB) AgentRuntimePut(data *meta.AgentRunTime) error {
	return orm.PutAgentRuntime(r, data)
}

func (r *DefaultDB) AgentRuntimeContainersUpdate(guid string, containers string) error {
	return orm.UpdateAgentRuntimeContainers(r, guid, containers)
}

func (r *DefaultDB) AgentRuntimeHostUpdate(guid string, hosts string) error {
	return orm.UpdateAgentRuntimeHosts(r, guid, hosts)
}

func (r *DefaultDB) AgentRuntimeStatusUpdate(guid string, status string) error {
	return orm.UpdateAgentRuntimeStatus(r, guid, status)
}

func (r *DefaultDB) ScheduleResultGet(guid string, data **meta.ScheduleResults) error {
	res, err := orm.ScheduleResultGet(r, guid)
	if err != nil {
		return err
	}
	*data = res
	return nil
}

func (r *DefaultDB) ScheduleResultPut(data *meta.ScheduleResults) error {
	if err := orm.SaveScheduleResult(r, data); err != nil {
		return err
	}
	return nil
}

func (r *DefaultDB) ScheduleResultDel(guid string) error {
	return orm.DeleteScheduleResult(r, guid)
}

func (r *DefaultDB) StrategyLinksGet(guid string, data **meta.StrategyLinks) error {
	var slks []meta.StrategyLinks
	if err := orm.StrategyLinksGet(r, guid, &slks); err != nil {
		return err
	}
	if len(slks) == 0 {
		return gerrors.DataNotFind
	}
	*data = &slks[0]
	return nil
}

func (r *DefaultDB) StrategyUnLinksGet(guid string, data *[]string) error {
	var slks []string
	if err := orm.StrategyUnLinksGet(r, guid, &slks); err != nil {
		return err
	}
	*data = slks
	return nil
}

func (r *DefaultDB) StrategyLinksPut(data *meta.StrategyLinks) error {
	return orm.SaveStrategyLinks(r, data.StrategyGuid, data.ApplicationGuids)
}

func (r *DefaultDB) MapGet(data **meta.Tree) error {
	var res = []struct {
		UpdateTime   string `json:"update_time" yaml:"update_time"`
		ResourceTree string `json:"resource_tree" yaml:"resource_tree"`
	}{}
	if err := orm.ResourceMap(r, &res); err != nil {
		return err
	}
	if len(res) == 0 {
		return gerrors.DataNotFind
	}
	*data = &meta.Tree{
		UpdateTime:   res[0].UpdateTime,
		ResourceTree: res[0].ResourceTree,
	}
	return nil
}

func (r *DefaultDB) MapPut(data *meta.Tree) error {
	return orm.SaveResourceTee(r, "glue", data.UpdateTime, data.ResourceTree)
}

func (r *DefaultDB) DagGet(root string, data **meta.Dag) error {
	var res = []struct {
		UpdateTime  string `json:"update_time" yaml:"update_time"`
		InstanceDag string `json:"instances_dag" yaml:"instances_dag" xorm:"instances_dag"`
	}{}

	if err := orm.ResourceDag(r, root, &res); err != nil {
		return err
	}
	if len(res) == 0 {
		return gerrors.DataNotFind
	}
	*data = &meta.Dag{
		UpdateTime:  res[0].UpdateTime,
		InstanceDag: res[0].InstanceDag,
	}
	return nil
}

func (r *DefaultDB) DagPut(root string, data *meta.Dag) error {
	return orm.SaveResourceDag(r, root, data.UpdateTime, data.InstanceDag)
}

func (r *DefaultDB) UpdateState(platform string, guid string) error {
	return orm.UpdateStatus(r, platform, guid)
}

func (r *DefaultDB) UpdateTrace(platform string, guid string) error {
	var instance *meta.Instance
	if err := r.InstanceGet(guid, &instance); err != nil {
		return err
	}
	return orm.UpdateTrace(r, platform, instance)
}

func (r *DefaultDB) MetricsInstancesList(kind string, data **meta.MetricsInstancesList) error {
	res, err := orm.MetricGuids(r, kind)
	if err != nil {
		return err
	}
	*data = &meta.MetricsInstancesList{
		GuidList: res,
	}
	return nil
}

const (
	CPUused     = "cpu_used"
	CPUusedlast = "cpu_used_last"
	Memused     = "mem_used"
	Memusedlast = "mem_used_last"

	TCPOpenTotal = "tcp_open_total"

	DagJaeger  = "glue"
	DagLinkerd = "glue_linkerd"
)

func (r *DefaultDB) MetricsMethodQuery(object, platform, metric, guid, method string, start, end int64, data **meta.Metrics) error {
	ms, err := orm.MetricsMethod(r, object, platform, metric, guid, method, start, end)
	if err != nil {
		return err
	}
	*data = &meta.Metrics{
		Metrics: ms,
	}
	return nil
}

func (r *DefaultDB) MetricsSumQuery(object, platform, metric, guid string, child string, start, end int64, data **meta.Metrics) error {
	ms, err := orm.MetricsSum(r, object, platform, metric, guid, child, start, end)
	if err != nil {
		return err
	}
	*data = &meta.Metrics{
		Metrics: ms,
	}
	return nil
}

func (r *DefaultDB) MetricsServiceMeshQuery(title, guid, platform string, data **meta.Metrics) error {
	switch title {
	case TCPOpenTotal:
		ms, err := orm.GetTCPOpenTotal(r, guid, platform)
		if err != nil {
			return err
		}
		*data = &meta.Metrics{
			Metrics: ms,
		}
		return nil
	default:
		return fmt.Errorf("unknown title")
	}
}

func (r *DefaultDB) MetricsQuery(object, platform, metric, guid, child string, start, end int64, data **meta.Metrics) error {
	ms, err := orm.Metrics(r, object, platform, metric, guid, child, start, end)
	if err != nil {
		return err
	}
	*data = &meta.Metrics{
		Metrics: ms,
	}
	return nil
}

func (r *DefaultDB) MetricsList(data **meta.Metrics) error {
	ms, err := orm.MetricsList(r)
	if err != nil {
		return err
	}
	*data = &meta.Metrics{
		Metrics: ms,
	}
	return nil
}

func (r *DefaultDB) MetricsQuerySpecial(title string, kind string, guid string, child string, start, end int64, data **meta.Metrics) error {
	switch title {
	case CPUused:
		ms, err := orm.CpuUsage(r, kind, guid, child, start, end)
		if err != nil {
			return err
		}
		*data = &meta.Metrics{
			Metrics: ms,
		}
		return nil
	case CPUusedlast:
		ms, err := orm.CpuUsageLast(r, kind, guid, child, start, end)
		if err != nil {
			return err
		}
		*data = &meta.Metrics{
			Metrics: ms,
		}
		return nil
	case Memused:
		ms, err := orm.MemoryUsage(r, kind, guid, child, start, end)
		if err != nil {
			return err
		}
		*data = &meta.Metrics{
			Metrics: ms,
		}
	case Memusedlast:
		ms, err := orm.MemoryUsageLast(r, kind, guid, child, start, end)
		if err != nil {
			return err
		}
		*data = &meta.Metrics{
			Metrics: ms,
		}
	}
	return nil
}

func (r *DefaultDB) MetricsPut(data interface{}) error {
	bts, ok := data.([]byte)
	if !ok {
		return errors.New("data must be byte")
	}
	//fmt.Println("write metrics")
	err1 := r.prom2.WriteData(bts)
	err2 := r.prom.WriteData(bts)
	if err1 == nil && err2 == nil {
		return nil
	}
	return fmt.Errorf("write to prometheus %v, write to influxdb %v", err1, err2)
}

const (
	K8sNodeInfo     = "k8s_node_info"
	K8sPodContainer = "k8s_pod_container"
)

//func (r *DefaultDB) InfosQuery(title string, guid string, data **meta.Info, args ...string) error {
//	switch title {
//	case K8sNodeInfo:
//		res, err := orm.K8sNodes(r, guid)
//		if err != nil {
//			return err
//		}
//		*data = &meta.Info{
//			Infos: res,
//		}
//		return nil
//	case K8sPodContainer:
//		res, err := orm.K8sPodContainers(r, guid, args[0], args[1])
//		if err != nil {
//			return err
//		}
//		*data = &meta.Info{
//			Infos: res,
//		}
//		return nil
//	}
//	return nil
//}

func (r *DefaultDB) LogsCounts(platform string, guid string, container string, start, stop, bucket string, data **meta.Logs) error {
	res, err := orm.LogsCounts(r, platform, guid, container, start, stop, bucket)
	if err != nil {
		return err
	}
	*data = &meta.Logs{
		LogBuckets: res,
	}
	return nil
}

func (r *DefaultDB) LogsQuery(platform string, guid string, container string, start, stop string, data **meta.Logs) error {
	res, err := orm.LogsInfo(r, platform, guid, container, start, stop)
	if err != nil {
		return err
	}
	*data = &meta.Logs{
		Logs: res,
	}
	return nil
}

func (r *DefaultDB) LogsPut(data interface{}) error {
	return r.influxdb.PutLogs(data)
	//return r.influxdb.Putk8sLogs(data)
}

func (r *DefaultDB) TracesPut(data interface{}) error {
	bts, ok := data.([]byte)
	if !ok {
		return errors.New("data must be byte")
	}
	return r.es.WriteSpan(bts)
}
func (r *DefaultDB) HelmRepoUpdates() error {
	return r.helm.UpdateChartRepos()
}

func (r *DefaultDB) GetIndex(data *string) error {
	var indexs []string
	if err := orm.GetIndexs(r, "glue", &indexs); err != nil {
		return err
	}
	if len(indexs) == 0 {
		return gerrors.DataNotFind
	}
	(*data) = indexs[0]
	return nil
}

func (r *DefaultDB) PutIndex(index string) error {
	return orm.PutIndexs(r, "glue", index)
}

// GetESClient ElasticSearch related
func (r *DefaultDB) GetESClient() *elastic.Client {
	return r.es.Client
}
